package com.msb.juc.c_022;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.List;

/**
 * 虚引用
 * 基本没用，不是给程序员用的，是给JVM用的
 * <p>
 * 虚引用被回收后，对象会装到QUEUE内，所以如果 QUEUE内有值，它就一定是虚引用被gc后的对象值
 * 虚引用的对象是不能 get到的，只能用来当作回收通知
 * <p>
 * 一个对象是否有虚引用的存在，完全不会对其生存时间构成影响，也无法通过虚引用来获取一个对象的实例
 * 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知
 * 虚引用和弱引用对关联对象的回收都不会产生影响，如果只有虚引用活着弱引用关联着对象，那么这个对象就会被回收。
 * 他们的不同之处在于弱引用的get方法，虚引用的get方法始终返回 null
 * 弱引用可以使用 ReferenceQueue，虚引用必须配合 ReferenceQueue使用
 * <p>
 * JDK中直接内存的回收就用到了虚引用，由于 JVM自动内存管理的范围是堆内存
 * 而直接内存是在堆内存之外的（其实是内存映射文件）所以直接内存的分配和回收都是由 Unsafe类去操作
 * Java在申请一块直接内存之后，会在堆内存分配一个对象保存这个堆外内存的引用，
 * 这个对象被垃圾说机器管理，一旦被回收，相应的用户线程就会收到通知并堆直接内存进行清理工作
 * <p>
 * 事实上，虚引用有一个很重要的用途就是用来做堆外内存的释放
 * DirectByteBuffer就是通过虚引用来实现堆外内存的释放的
 */
public class T06_PhantomReference {

    private static final List<Object> LIST = new LinkedList<>();
    private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        PhantomReference<M> phantomReference = new PhantomReference<>(new M(), QUEUE);

        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024 * 1024]);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                System.out.println(phantomReference.get());
            }
        }).start();

        new Thread(() -> {
            while (true) {
                Reference<? extends M> poll = QUEUE.poll();
                if (poll != null) {
                    System.out.println(" 虚引用对象被 JVM回收了 " + poll);
                }
            }
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
